package com.numericalmethod.algoquant.model.ralph2009.demo;

import java.text.DecimalFormat;

import javax.swing.JComponent;
import javax.swing.JFrame;

import com.numericalmethod.algoquant.model.ralph2009.Ralph2009OptimizedPortfolio;
import com.numericalmethod.algoquant.model.ralph2009.Ralph2009OptimizerStatusListener;
import com.numericalmethod.algoquant.model.ralph2009.Ralph2009PortfolioConstraint;
import com.numericalmethod.algoquant.model.ralph2009.Ralph2009PortfolioOptimizer;
import com.numericalmethod.algoquant.model.ralph2009.Ralph2009SimulationParameters;
import com.numericalmethod.algoquant.model.ralph2009.market.MarketState;
import com.numericalmethod.algoquant.model.ralph2009.market.Ralph2009MarketSDE;


/**
 * 
 * Demonstrate the portfolio optimization algorithm described in 
 * 
 * Momentum and Mean Reversion in Strategic Asset Allocation (Ralph et al. 2009)
 * 
 * This demo shows how the optimal allocation of stocks are affected as compared to the myopic stock allocation strategy
 * when the stock price dynamics are simultaneously modeled with Momentum(with phi > 0) and 
 * MeanReverion(i.e. with dividend yield updated information affecting the strategic allocation of stocks.)
 * 
 * @author Paul/Clement/Stephen
 *
 */
public class Ralph2009PortfolioOptimizationDemo {

	public static void main(String[] args) {

		// output the dynamic weighting vs myopic weighting 
		// for some model parameters.
		dynamicVersusMyopicDemo();
		
	}
	
	private static void dynamicVersusMyopicDemo() {

		double gamma = 5.0; // CRRA utility function parameter
		
		//model parameters for stock price, dividend yield and Momentum process evolution 
		double riskFreeRate=0.002;
		double phi = 0.39; // how much the price process is on momentum.
		double mu0=1.15/100; // the intrinsic drift factor in the geometric brownian motion
		double mu1=0.012; // the additional drift factor that depends on the excess log dividiend yield
		double alpha = 0.0094; // rate of mean reversion of the excess log dividend yield
		// check equation 16 of Ralph 2009 to see why there are sigma 1 and 2.
		double sigmaS1=0.0628; // volatility 1 of the asset price.
		double sigmaS2=0.0; // volatility 2 of asset price.
		double sigmaX1=-0.0843; // volatility 1 of the excess log dividend yield
		double sigmaX2=0.0152;	// volatility 2 of the excess log dividend yield	

		
		// create the Ralph 2009 model
		Ralph2009MarketSDE sde = new Ralph2009MarketSDE(mu0, mu1, phi, alpha, 
				sigmaS1, sigmaS2, sigmaX1, sigmaX2);
		
		double dt = 1.0/12.0; // monthly trading frequency
		double investmentHorizon = 10.0; // investment horizon for 10 years
		
		// number of simulations
		int paths=100000;
		
		// create the simulation parameters object.
		Ralph2009SimulationParameters param = new Ralph2009SimulationParameters(
				113221387502258959L, 1733312199225432L, dt, paths);
		
		boolean allowShortsell=true;
		boolean allowLeverage=true;
		Ralph2009PortfolioConstraint constraint=new Ralph2009PortfolioConstraint(allowShortsell,allowLeverage);

		// create the optimizer based on Ralph 2009 and Brandt 2005 algorithm.
		Ralph2009PortfolioOptimizer ralph2009Optimizer = new Ralph2009PortfolioOptimizer(sde, 
				param, investmentHorizon, constraint); 

		// register the listener for status display of the simulation
		ralph2009Optimizer.addStatusListener(
			new Ralph2009OptimizerStatusListener() {
				DecimalFormat df = new DecimalFormat("#.######"); //display 6 d.p.

				public void weightAtTimeIndexComputed(int timeIndex, int numberOfTimeSteps, Ralph2009OptimizedPortfolio p) {
					// print the weight computation status
					// we compute (T-1 steps, index at zero, so we minus numberOfTimeSteps by 2)
					System.out.println("t="+timeIndex+"/"+(numberOfTimeSteps-2)
							+" computed. Dynamic weight = "+df.format(p.getDynamicWeightAt(timeIndex))
							+", Myopic weight = "+df.format(p.getMyopicWeightAt(timeIndex))
							+", Correlation = "+df.format(p.getCorrelationAt(timeIndex)));
				}
				
				public void pathsSimulated(int pathsCompleted, int totalNumberOfPaths) {
					// print the simulation status
					if (pathsCompleted%2000==0) 
						System.out.println("Paths simulated: "+pathsCompleted+"/"+totalNumberOfPaths);
				}
			}
		);

		// initial market state values.
		double s0 = 100; 
		double m0 = 0.03;		
		double x0 = 0.02; 

		MarketState currentMarketState = new MarketState(s0,m0,x0); 
		
		// solve the optimal portfolio weight.
		Ralph2009OptimizedPortfolio result=ralph2009Optimizer.findOptimalWeights(
				currentMarketState, 
				riskFreeRate, gamma);

		// get the time step 0 weight 
		// (i.e. allocation weight we should assign to the stock based on the parameters)
		
		// dynamic weight
		// (the weighting to use if we assume the excess dividend yield and momentum has effect to portfolio allocation)
		double dynamicWeight = result.getDynamicWeightForTrading();
		
		// myopic weight 
		// (the weighting to use if we assume the dividend yield and momentum has no relation to portfolio allocation)
		double myopicWeight = result.getMyopicWeightForTrading();
		
		// display results
		System.out.println();
		System.out.println("Demo for dynamic weighting vs myopic weighting");
		System.out.println();
		System.out.println("Simulation paths = "+paths);
		System.out.println();
		//System.out.println("Input model:\n"+model);
		System.out.println("\nDynamic Weight for trading today: "+dynamicWeight);
		System.out.println("Myopic Weight for trading today: "+myopicWeight);
		System.out.println();
		
		// plot the result on screen
		JComponent chart=ResultsPlottingHelper.plotWeights(result, "Dynamic vs Myopic Weights using Ralph2009 Model");
		JFrame f=new JFrame("Ralph2009 Portfolio Optimization Demo");
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.getContentPane().add(chart);
		f.pack();
		f.setVisible(true);
		
	}
	


	
	

	
}
